#include "Thread.hpp"
#include <vector>
#include "LockGuard.hpp"
#include <mutex>


using namespace ThreadModule;

pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;

int g_tickets = 10000; // 共享资源，没有保护

class ThreadData
{
public:
    ThreadData(int &tickets, const std::string &name, pthread_mutex_t &mutex)
        : _tickets(tickets), _name(name), _mutex(mutex), _total(0)
    {
    }
    ~ThreadData()
    {
    }

public:
    int &_tickets; // 所有线程都会引用同一个全局变量
    std::string _name;
    int _total;
    pthread_mutex_t _mutex;
};

void routes(ThreadData *td)
{
    while (true)
    {
        // LockGuard guard(&td->_mutex); // 临时对象 RAII风格的加锁和解锁

        std::lock_guard<std::mutex> lock(&td->_mutex); // C++11的锁

        if (td->_tickets > 0)
        {
            usleep(1000);
            printf("%s running, get tickets: %d\n", td->_name.c_str(), td->_tickets); // 2
            td->_tickets--;                                                           // 3
            pthread_mutex_unlock(&gmutex);                                            // 解锁
            td->_total++;
        }
        else
        {
            break;
        }
    }
}
const int num = 4;
int main()
{
    // 局部锁
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, nullptr);

    std::vector<Thread<ThreadData *>> threads;
    std::vector<ThreadData *> datas;
    // 1、创建一批线程
    for (int i = 0; i < num; i++)
    {
        std::string name = "thread-" + std::to_string(i + 1);
        ThreadData *td = new ThreadData(g_tickets, name, mutex);
        threads.emplace_back(routes, td, name);
        datas.emplace_back(td);
    }
    // 2、启动一批线程
    for (auto &thread : threads)
    {
        thread.Start();
    }
    // 3、等待一批线程
    for (auto &thread : threads)
    {
        thread.Join();
    }
    sleep(1);
    for (auto &data : datas)
    {
        std::cout << data->_name << " : " << data->_total << std::endl;
        delete data;
    }
    return 0;
}


/*
// 全局锁（静态锁）
pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;

// 数据不一致
int g_tickets = 10000; // 共享资源，没有保护

class ThreadData
{
public:
    ThreadData(int &tickets, const std::string &name = "none-thread")
        : _tickets(tickets), _name(name)
    {
    }
    ~ThreadData()
    {
    }

public:
    int &_tickets; // 所有线程都会引用同一个全局变量
    std::string _name;
    int _total;
};

void routes(ThreadData *td)
{
    while (true)
    {
        // 访问临界资源的代码，叫做临界区 --- 我们加锁，本质就是把并行执行---改为串行执行 ==>>>加锁的力度要越细越好（即只保护临界区，非临界区代码不要加到锁里）
        // 像以下1，2，3都是访问的临界区
        pthread_mutex_lock(&gmutex); // 加锁
        if (td->_tickets > 0)        // 1
        {
            usleep(1000);
            printf("%s running, get tickets: %d\n", td->_name.c_str(), td->_tickets); // 2
            td->_tickets--;                                                           // 3
            pthread_mutex_unlock(&gmutex);                                            // 解锁
            td->_total++;
        }
        else
        {
            pthread_mutex_unlock(&gmutex); // 解锁
            break;
        }

        // std::cout << "&tickets: " << &tickets << std::endl;
        // sleep(1);
    }
}
const int num = 4;

int main()
{
    std::vector<Thread<ThreadData *>> threads;
    std::vector<ThreadData *> datas;
    // 1、创建一批线程
    for (int i = 0; i < num; i++)
    {
        std::string name = "thread-" + std::to_string(i + 1);
        ThreadData *td = new ThreadData(g_tickets, name);
        threads.emplace_back(routes, td, name);
        datas.emplace_back(td);
    }

    // 2、启动一批线程
    for (auto &thread : threads)
    {
        thread.Start();
    }

    // 3、等待一批线程
    for (auto &thread : threads)
    {
        thread.Join();
        // std::cout << "wait thread done, thread is: " << thread.name() << std::endl;
    }

    sleep(1);
    for (auto &data : datas)
    {
        std::cout << data->_name << " : " << data->_total << std::endl;
        delete data;
    }

    return 0;
}
*/

/*
using namespace ThreadModule;

// 数据不一致
int g_tickets = 10000; // 共享资源，没有保护

class ThreadData
{
public:
    ThreadData(int &tickets, const std::string &name = "none-thread")
        : _tickets(tickets), _name(name)
    {
    }
    ~ThreadData()
    {
    }

public:
    int &_tickets; // 所有线程都会引用同一个全局变量
    std::string _name;
    int _total;
};

void routes(ThreadData *td)
{
    while (true)
    {
        if (td->_tickets > 0)
        {
            // 模拟一次抢票的逻辑
            usleep(1000);
            printf("%s running, get tickets: %d\n", td->_name.c_str(), td->_tickets);
            td->_tickets--;
            td->_total++;
        }
        else
        {
            break;
        }

        // std::cout << "&tickets: " << &tickets << std::endl;
        // sleep(1);
    }
}
const int num = 4;

int main()
{
    std::vector<Thread<ThreadData *>> threads;
    std::vector<ThreadData *> datas;
    // 1、创建一批线程
    for (int i = 0; i < num; i++)
    {
        std::string name = "thread-" + std::to_string(i + 1);
        ThreadData *td = new ThreadData(g_tickets, name);
        threads.emplace_back(routes, td, name);
        datas.emplace_back(td);
    }

    // 2、启动一批线程
    for (auto &thread : threads)
    {
        thread.Start();
    }

    // 3、等待一批线程
    for (auto &thread : threads)
    {
        thread.Join();
        // std::cout << "wait thread done, thread is: " << thread.name() << std::endl;
    }

    sleep(1);
    for (auto &data : datas)
    {
        std::cout << data->_name << " : " << data->_total << std::endl;
        delete data;
    }

    return 0;
}*/

// 线程也是可以不用主线程阻塞等待的，只需要把自己设为分离状态， 主线程就不可以等待了
// 线程分离，线程的底层仍然属于同一个线程，只是不需要等待了
// main thread 应该是最后一个退的 如果线程是分离的，那主线程则一般都是死循环的：常驻任务，不退的
// #include <iostream>
// #include <pthread.h>
// #include <unistd.h>

// std::string ToHex(pthread_t tid)
// {
//     char id[64];
//     snprintf(id, sizeof(id), "0x%lx", tid);
//     return id;
// }
// // 全局变量属于共享资源，一个线程修改另外的线程拿到的一是修改后的数据，如果不想都被修改，则可以添加__thread关键字
// // int g_val = 100;
// __thread int g_val = 100;

// void *threadrun1(void *args)
// {
//     // 自己把自己分离了
//     // pthread_detach(pthread_self());

//     std::string name = static_cast<const char *>(args);
//     while (true)
//     {
//         printf("%s, g_val: %d, &g_val: %p\n", name.c_str(), g_val, &g_val);
//         sleep(1);
//     }
//     // int cnt = 5;
//     // while (true)
//     // {
//     //     // if(!(cnt--)) break;
//     //     // std::cout << "I am a new  thread ..." "tid: " << pthread_self() << ", ToHex(tid): " << ToHex(pthread_self()) << std::endl;
//     //     std::cout << "I am thread: "<< name << ", " << getpid() << ", cnt: " << cnt << ", &cnt: " << &cnt << std::endl;
//     //     sleep(1);
//     //     // int a = 10;
//     //     // a /= 0;
//     // }

//     return nullptr;
// }
// void *threadrun2(void *args)
// {
//     std::string name = static_cast<const char *>(args);
//     while (true)
//     {
//         printf("%s, g_val: %d, &g_val: %p\n", name.c_str(), g_val, &g_val);
//         g_val--;
//         sleep(1);
//     }
//     return nullptr;
// }

// int main()
// {
//     pthread_t tid1;
//     pthread_t tid2;
//     pthread_create(&tid1, nullptr, threadrun1, (void *)"thread 1");
//     pthread_create(&tid2, nullptr, threadrun2, (void *)"thread 2");

//     pthread_join(tid1, nullptr);
//     pthread_join(tid2, nullptr);

//     // pthread_detach(tid);
//     // std::cout << "I am s main thread ..." << "tid: " << pthread_self() << ", ToHex(tid): " << ToHex(pthread_self()) << std::endl;
//     // std::cout << "I am s new  thread ..." << "tid: " << tid << ", ToHex(tid): " << ToHex(tid) << std::endl;
//     // int cnt = 1;
//     while (true)
//     {
//         // if (!(cnt--)) break;
//         sleep(1);
//     }
//     // 主线程也可以分离指定线程
//     // pthread_detach(tid);

//     // std::cout << "main thread wait block" << std::endl;
//     // pthread_join(tid, nullptr);
//     // std::cout << "main thread wait return" << std::endl;

//     return 0;
// }

// C++里的多线程 实际上就是封装的原生线程库，所以仍然要链接原生线程库
// #include <thread>
// #include <unistd.h>

// void threadrun(int num)
// {
//     while (num)
//     {
//         std::cout << "I am a thread, num : " << num << std::endl;
//         sleep(1);
//     }
// }

// int main()
// {
//     std::thread t1(threadrun, 10);
//     std::thread t2(threadrun, 10);
//     std::thread t3(threadrun, 10);
//     std::thread t4(threadrun, 10);
//     std::thread t5(threadrun, 10);

//     while (true)
//     {
//         std::cout << "I am a main thread" << std::endl;
//         sleep(1);
//     }

//     t1.join();
//     t2.join();
//     t3.join();
//     t4.join();
//     t5.join();

//     return 0;
// }

// #include <iostream>
// #include <unistd.h>
// #include <sys/types.h>
// #include <string>
// #include <vector>
// #include <cstdlib>
// #include <pthread.h> // 原生线程库头文件

// const int threadnum = 5;

// class Task
// {
// public:
//     Task(int x, int y):datax(x), datay(y)
//     {}
//     ~Task()
//     {}
//     int Excute()
//     {
//         return datax + datay;
//     }
// private:
//     int datax;
//     int datay;
// };
// class Result
// {
// public:
//     Result(){}
//     ~Result(){}

//     void SetResult(int result, const std::string &threadname)
//     {
//         _result = result;
//         _threadname = threadname;
//     }

//     void Print()
//     {
//         std::cout << _threadname << " : " << _result << std::endl;
//     }
// private:
//     int _result;
//     std::string _threadname;
// };

// class ThreadData
// {
// public:
//     ThreadData(int x, int y, const std::string& threadname)
//         : _t(x,y), _threadname(threadname)
//     {}
//     std::string threadname()
//     {
//         return _threadname;
//     }
//     int run()
//     {
//         return _t.Excute();
//     }
// private:
//     std::string _threadname;
//     Task _t;
// };
// void* handlerTask(void *args)
// {
//     ThreadData *td = static_cast<ThreadData*>(args);

//     std::string name = td->threadname();

//     Result *res = new Result();

//     int result = td->run();

//     res->SetResult(result, name);

//     // std::cout << name << " run result : " << result << std::endl;
//     delete td;
//     return nullptr;
// }
// int main()
// {
//     std::vector<pthread_t> threads;

//     // 创建多线程时，不要往线程中传入同一个地址，要开辟不同的空间，因为线程共享数据，就会导致问题
//     for (int i = 0; i < threadnum; i++)
//     {
//         char threadname[64];
//         snprintf(threadname, 64, "Thread-%d", i + 1);

//         ThreadData *td = new ThreadData(10, 20, threadname);

//         pthread_t tid;
//         // 在传参时，传给执行函数的最后一个参数没人规定必须时线程名字，可以创一个类对象过去，就能带过去更多的信息
//         pthread_create(&tid, nullptr, handlerTask, td);
//         threads.push_back(tid);
//     }
//     std::vector<Result*> result_set;
//     void* ret = nullptr;
//     for (auto tid : threads)
//     {
//         // 同样 在回收线程的传参时，传给join函数的最后一个参数没人规定必须是线程返回值，由于整个参数是void**所以可以接收任何类型的变量包括类对象，则能带过回来更多的信息
//         pthread_join(tid, &ret);
//         result_set.push_back((Result*)ret);
//     }

//     for(auto & res : result_set)
//     {
//         res->Print();
//         delete res;
//     }

//     return 0;
// }

// void* handlerTask(void *args)
// {
//     // std::string threadname = static_cast<char*>(args);
//     const char *threadname = static_cast<char *>(args); // 由于线程拿到的是外部的缓冲区，主线程已经改了，但是新线程还拿着那个地址去访问，所以这里的线程名就会出现错乱
//     while (true)
//     {
//         sleep(2);
//         std::cout << "I am " << threadname << std::endl;
//     }

//     delete[] threadname;
//     return nullptr;
// }

// 多线程创建
// int main()
// {
//     std::vector<pthread_t> threads;

//     // 创建多线程时，不要往线程中传入同一个地址，要开辟不同的空间，因为线程共享数据，就会导致问题
//     for (int i = 0; i < threadnum; i++)
//     {
//         // char threadname[64];
//         char *threadname = new char[64];
//         snprintf(threadname, 64, "Thread-%d", i + 1);
//         pthread_t tid;
//         // pthread_create(&tid, nullptr, handlerTask, threadname);
//         threads.push_back(tid);
//     }
//     for (auto tid : threads)
//     {
//         pthread_join(tid, nullptr);
//     }

//     return 0;
// }

/*
using namespace std;

// 同一个进程内的线程，大部分资源都是共享的
int g_val = 100;
string ToHex(pthread_t tid)
{
    char id[64];
    snprintf(id, sizeof(id), "0x%lx", tid);
    return id;
}
// 线程退出
// 1、代码跑完，结果对
// 2、代码跑完，结果不对
// 3、代码异常  --重点--  任意一个线程出异常了，都会导致整个进程退出 ------ 多线程代码往往健壮性不好
void *threadrun(void *arg)
{
    string threadname = (char *)arg;
    int cnt = 5;
    while (cnt)
    {
        printf("new thread, g_val: %d, &g_val: %p\n", g_val, &g_val);
        // std::cout << threadname << " is runing, " << cnt  << ", pid: " << getpid()
        // << " mythread id: " << ToHex(pthread_self())
        // << " g_val: " << g_val << " &g_val: " << &g_val << std::endl;
        cnt--;
        g_val--;
        sleep(1);

        // 故意出异常来测试
        // int* p = nullptr;
        // *p = 100;
    }

    // 线程退出 有三种方式
    pthread_exit((void*)123);
    // exit(10); // 不能用exit，否则会直接退整个进程

    // return (void*)123;
}
int main()
{
    // 创建线程id 无需像进程id一样还需要用户来设置，这里直接作为输出型参数传到线程创建函数里就行
    pthread_t tid;
    pthread_create(&tid, nullptr, threadrun, (void*)"thread-1");
    void* ret = nullptr;

    int cnt = 10;
    // while (true)
    // {
    //     printf("main thread, g_val: %d, &g_val: %p\n", g_val, &g_val);
    //     // cout << "main thread is runing, " << cnt << ", pid: " << getpid()
    //     //         << " mythread id: " << ToHex(pthread_self())
    //     //         << " g_val: " << g_val << " &g_val: " << &g_val << std::endl;
    //     cnt--;
    //     sleep(1);
    // }
    // sleep(2);
    // pthread_cancel(tid);
    // 第二个参数 retval（一个输出型参数） 是线程执行函数的返回值，由于threadrun返回值是void*,所以用void**来接收
    // int pthread_join(pthread_t thread, void **retval);
    // 为什么要用一个二级指针来获得一个退出码呢，进程都是返回的信号，能说明进程出现了什么问题，那是因为线程出异常了会导致整个进程退出，而进程是相互独立的
                           // 内存中是开辟了空间的
    int n = pthread_join(tid, &ret);
    std::cout << "main thread quit, n = " << n <<", main thread get a ret : " << (long long)ret << std::endl;

    return 0;
}
*/